iT邦幫忙

2021 iThome 鐵人賽

DAY 26
1
Modern Web

初學者跪著學JavaScript系列 第 26

初學者跪著學JavaScript Day26 : 認識生成器,chris不生氣

  • 分享至 

  • xImage
  •  

一日客語:中文:星 客語:sen24 sam三聲

簡單了解:

1.生成器:function* name /function *name
2.yield運算子搭配可以暫停
3.next()恢復執行

定義方式:

  1. 宣告function
  2. function 表達式
  3. 生成器定義在物件內
  4. 生成器定義在class內
  5. 無法使用箭頭函式
//宣告function
function* animal() {}
console.log(animal());

//function 表達式
const animalx = function* () {};
console.log(animalx());

//生成器定義在物件內
const myObject = {
    *animal() {},
};
console.log(myObject.animal());

//生成器定義在class內
class Zoo {
    *animal() {}
}

const myanimal = new Zoo();
console.log(myanimal.animal());

生成器可以對process 暫停/恢復

使用yield 生成器就暫停執行,耐心等待另一個請求(next)進來

function* mygen() {
    console.log('執行第一句');   //1
    yield; //暫停可以使用yield   //2
    console.log('執行第一句');   //3 
}

要如何使用?

  1. function 內使用yield來暫停
    generator object 使用next()繼續

  2. 無法透過呼叫mygen使用生成器,要透過迭代器來控制

    要呼叫生成器會建立出迭代器, 像是mygen()會回傳 generator object

    利用迭代器呼叫生成器來控制process

  3. 給外部使用的方法叫next(),返回值是一個物件
    {value:xxx,done:true/false},向生成器要一個值(value)
    done是指生成器的目前狀態,當done是true 時,表示function已經執行完畢,若是false時,表示function尚未執行完畢還可以進行


注意:直接透過生成器使用next方法

會一直重跑第一句

function* mygen() {
    console.log('執行第一句'); //1
    yield; //暫停             //2
    console.log('執行第三句'); //3
}

mygen().next(); //執行第一句
mygen().next(); //執行第一句

使用方式:生成器放到變數裡使用

mygen()到變數a,使用變數a來操控

function* mygen() {
    yield; 
}
const a = mygen();
a.next(); 

執行順序

第一個next(),會開始執行跑每一行直到遇到第一yield,碰到yield會返回value,生成器會一直等到出現第二個next()


來源:A Simple Guide to Understanding Javascript (ES6) Generator

function* mygen() {
    console.log('執行第一句'); //1
    yield; //暫停             //2
    console.log('執行第三句'); //3
}

const a = mygen();
a.next(); //{ value: undefined, done: false }
a.next(); //{ value: undefined, done: true }

第一個:a.next():在生成器內重頭開始

印出執行第一句

遇到yield暫停,返回value

第二個:a.next(),從暫停下一行繼續開始


yield可以透過next丟出一個值

generator object.next() 會返回一個物件,物件屬性名會有value 和done

function* mygen() {
    console.log('執行第一句'); //1
    yield '起床'; //暫停   //2
    console.log('執行第三句'); //3
    yield '刷牙';
}

const a = mygen();

let state1 = a.next(); //執行第一句
console.log(state1); //{ value: '起床', done: false }
let state2 = a.next(); //執行第三句
console.log(state2); //{ value: '刷牙', done: false }

最後一個next 是undefined 因為yield沒有值且沒有return值

function* Pig() {
    yield '吃早餐';
    yield '睡一下';
    yield '吃點心';
    yield '睡一下';
}

let pigGen = Pig();
console.log(pigGen.next()); //{ value: '吃早餐', done: false }
console.log(pigGen.next()); //{ value: '睡一下', done: false }
console.log(pigGen.next()); //{ value: '吃點心', done: false }
console.log(pigGen.next()); //{ value: '睡一下', done: false }
console.log(pigGen.next()); //{ value: undefined, done: true }

當function 內有返回值時
可以看到最後一句{ value: '豬', done: true }的value不是undefined會是
return '豬';

function* Pig() {
    yield '吃早餐';
    yield '睡一下';
    yield '吃點心';
    yield '睡一下';
    return '豬';
}

let pigGen = Pig();
console.log(pigGen.next()); //{ value: '吃早餐', done: false }
console.log(pigGen.next()); //{ value: '睡一下', done: false }
console.log(pigGen.next()); //{ value: '吃點心', done: false }
console.log(pigGen.next()); //{ value: '睡一下', done: false }
console.log(pigGen.next()); //{ value: '豬', done: true }

當return後面還有內容呢?

後面都會被忽略掉,就算後面有yield也會是undefined

function* Pig() {
    yield '吃早餐';
    return '豬';
    yield '睡覺';
}

let pigGen = Pig();
console.log(pigGen.next()); //{ value: '吃早餐', done: false }
console.log(pigGen.next()); //{ value: '豬', done: true }
console.log(pigGen.next()); //{ value: undefined, done: true }

說說yield

算是運算子一種

yield a + b + c;

因為+優先權高於 yield所以可以不用括號

會自動視為 yield (a + b + c);

但遇到運算子優些權高於yield 就要括號摟,因為yield優先權超低的

運算子的優先順序

console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield 123)); // OK

yield 可以委託給別的生成器

使用方式: yield* 生成器

function* Cat() {
    yield '喵喵喵';
    yield '喵咪睡午覺';
}
function* Pig() {
    yield '吃早餐';
    yield '睡覺';
    yield* Cat();
    yield '齁齁齁';
    return '結束';
}

let pigGen = Pig();
console.log(pigGen.next());
console.log(pigGen.next());
console.log(pigGen.next());
console.log(pigGen.next());
console.log(pigGen.next());
console.log(pigGen.next());

//{ value: '吃早餐', done: false }
//{ value: '睡覺', done: false }
//{ value: '喵喵喵', done: false }
//{ value: '喵咪睡午覺', done: false }
//{ value: '齁齁齁', done: false }
//{ value: '結束', done: true }

說說next可以傳參數(容易搞混的地方)

Generator.prototype.next()
語法:next(value)
mdn:

The value to send to the generator.
The value will be assigned as a result of a yield expression. For example, in variable = yield expression, the value passed to the .next() function will be assigned to variable.

next的value值會賦值給 yield expression的結果

next()會回傳一個物件,( )內可以傳參數,傳送到yield 表達式賦予的變數上


將 yield 表達式賦值到一個變數上,使用next(value)語法內的value會傳到這個變數

function* Pig() {
    let value1 = yield 100;
    console.log(value1);
    yield 200;
}
const littlePig = Pig();
console.log(littlePig.next('好吃好吃'));
console.log(littlePig.next('吃24hr'));
console.log(littlePig.next());

//{ value: 100, done: false }
//吃24hr
//{ value: 200, done: false }
//{ value: undefined, done: true }

圖示解說:

所以value1會是'吃24hr'


牛刀小試一下:運用在for-loop

for (let i = 5; i >= 1; i--) {
    console.log(i);
}
//5
//4
//3
//2
//1

以下情境只是幻想:
codereview時
饅頭大人chris爆炸前要趕快關懷饅頭大人的胃,
如果使用for迴圈會一直問肚子餓不餓,肯定會更生氣!!
若使用生成器可以隔一段時間再詢問,尤其在血糖急據降低的時侯投食才是恰到好處作法

function* genforloop(n) {
    for (let i = 1; i <= 5; i++) {
        let dessert = yield `第${i}次問chris肚子餓不餓`;
        console.log(`要不要吃${dessert},chris:非常需要`);
    }
    yield;
    return '絕對不生氣,萬歲';
}

let mygen = genforloop(5);
mygen.next();
console.log('code review 15min');
mygen.next('oreo');
console.log('code review 30min');
mygen.next('麥當勞');
console.log('code review 1hr');
mygen.next('炸雞');
console.log('code review 1hr15min');
mygen.next('豆花');
console.log('code review 1hr30min');
mygen.next('紅豆餅');
console.log(mygen.next());

//code review 15min
//要不要吃oreo,chris:非常需要
//code review 30min
//要不要吃麥當勞,chris:非常需要
//code review 1hr
//要不要吃炸雞,chris:非常需要
//code review 1hr15min
//要不要吃豆花,chris:非常需要
//code review 1hr30min
//要不要吃紅豆餅,chris:非常需要
//{ value:'絕對不生氣,萬歲', done: true }

感謝饅頭大人當我文章主角
也推薦安迪大大的寫的,如何讓工具人帥一波
傳送門:
方函式的能力展現:認識生成器,工具人更神氣(上)
方函式的能力展現:認識生成器,工具人更神氣(下)

剩下四天了~真是太累了~

參考資料:
Exploring ES6
[譯]什麼是JavaScript生成器?如何使用生成器?
generator的next传参注意点
忍者開發技巧探秘第二版


上一篇
初學者跪著學JavaScript Day25 : 寧願找this也不碰歷史
下一篇
初學者跪著學JavaScript Day27 : 渣男給不完的promise
系列文
初學者跪著學JavaScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
南國安迪
iT邦新手 3 級 ‧ 2021-10-11 20:01:00

感謝大師業配XD

wendy iT邦新手 2 級 ‧ 2021-10-11 20:17:05 檢舉

工具人之神

再次強調故事來源非作者本人

妳明白吧?

我要留言

立即登入留言